﻿//-----------------------------------------------------------------------
// <copyright file="UserAuthorizationServiceBase.cs" company="Alan Rodrigues">
//     Copyright (c) Alan Rodrigues. All rights reserved.
// </copyright>
// <developed>Alan Rodrigues (operninha@gmail.com)</developed>
//-----------------------------------------------------------------------

namespace Authorization
{
    using System;
    using System.Linq;
    using System.ServiceModel;
    using Properties;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;

    internal static class UserAuthorizarionServiceBase
    {
        #region UserAuthorizationService With UserName Parameter

        #region OperationCollection

        public static OperationCollection GetOperationsForUser(string userName, string applicationName, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");

            using (var context = GetUserAuthorization(userName, applicationName))
            {
                return context.GetOperations(scopeName);
            }
        }

        public static string[] GetOperationNamesForUser(string userName, string applicationName, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            return GetUserAuthorization(userName, applicationName).GetOperationNames(scopeName);
        }

        #endregion

        #region TaskCollection

        public static TaskCollection GetTasksForUser(string userName, string applicationName, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            return GetUserAuthorization(userName, applicationName).GetTasks(scopeName);
        }

        public static string[] GetTaskNamesForUser(string userName, string applicationName, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            return GetUserAuthorization(userName, applicationName).GetTaskNames(scopeName);
        }

        public static bool IsUserInTask(string userName, string applicationName, string taskName, string[] scopeNames)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            SecUtility.CheckParameter(ref taskName, true, true, false, 64, "taskName");

            using (var context = GetUserAuthorization(userName, applicationName))
            {

                if (scopeNames == null)
                    return context.IsUserInTask(taskName, null);

                foreach (var scopeName in scopeNames)
                {
                   if (string.IsNullOrEmpty(scopeName) || context.Application.ScopeExists(scopeName))
                       if (context.IsUserInTask(taskName, scopeName == "" ? null : scopeName))
                            return true;
               }
                
            }

            return false;
        }

        #endregion

        #region RoleCollection

        public static RoleAssignmentCollection GetRoleAssignmentsForUser(string userName, string applicationName, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");            
            using (var context = GetUserAuthorization(userName, applicationName))
            {
                return context.GetRolesAssignments(scopeName);
            }            
        }

        public static bool IsUserInRoleAssignment(string userName, string applicationName, string scopeName, string roleName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            SecUtility.CheckParameter(ref roleName, true, true, false, 64, "roleName");
                
            using (var context = GetUserAuthorization(userName, applicationName))
            {
                return context.IsInRoleAssignment(scopeName, roleName);
            }

            //string[] roles = null;

            //using (AuthorizationContext context = GetUserAuthorization(userName, applicationName) )
            //{
            //   roles = context.GetRoleAssignmentNames(scopeName);
            //}
            
            ////roles = GetUserAuthorization(userName, applicationName).GetRoleAssignmentNames(scopeName);

            //return roles != null && roles.Contains(roleName, StringComparer.OrdinalIgnoreCase);
        }

        public static string[] GetRoleAssignmentNamesForUser(string userName, string applicationName, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            return GetUserAuthorization(userName, applicationName).GetRoleAssignmentNames(scopeName);
        }

        #endregion

        #region Scope
        
        /// <summary>
        /// The GetAssignedScopesPage method retrieves a list of the scopeNames in which the client represented by the current <see cref="AuthorizationContext"/> object is assigned to at least one role.
        /// </summary>
        /// <param name="pageSize">The number of elements in each page operations.</param>
        /// <param name="cursor">A pointer to a cursor that represents the current page of results. For the first call to the GetAssignedScopesPage method, pass null as the value for this parameter to retrieve the first page of results. The number of elements on a page is determined by the value of the PageSize parameter. On output, this parameter contains the value to be passed in the next call to GetAssignedScopesPage to retrieve the next page of results. If the value of this parameter on output is null, there are no more operations pages. </param>
        /// <returns></returns>
        public static string[] GetAssignedScopesPageForUser(string userName, string applicationName, int pageSize, ref string cursor)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            return GetUserAuthorization(userName, applicationName).GetAssignedScopesPage(pageSize, ref cursor);
        }


        public static string[] GetAllAssignedScopesForUser(string username, string applicationName, string filterPattern)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            AuthorizationContext context = GetUserAuthorization(username, applicationName);
            string cursor = null;
              
            List<string> scopes = new List<string>();

            if (filterPattern != null)
            {
                Regex regex = new Regex(filterPattern, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace);

                do
                {
                    string[] scopesArray = context.GetAssignedScopesPage(10000, ref cursor);
                    foreach (var item in scopesArray)
                    {
                        if (regex.IsMatch(item))
                            scopes.Add(item);
                    }

                }
                while (cursor != null);
            }
            else //se nao existe filtro, usar AddRange e não testar o filtro para maior velocidade
            {
                do
                {
                    string[] scopesArray = context.GetAssignedScopesPage(10000, ref cursor);
                    scopes.AddRange(scopesArray);
                }
                while (cursor != null);
             }

            return scopes.ToArray();
        }

        #endregion

        #region AccessCheck

        public static bool AccessCheck(string userName, string applicationName, int operationID, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            return GetUserAuthorization(userName, applicationName).AccessCheck(operationID, scopeName);
        }

        public static bool AccessCheck(string userName, string applicationName, string operationName, string scopeName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            SecUtility.CheckParameter(ref operationName, true, true, false, 64, "operationName");

            Operation operation = AuthorizationStoreManager.GetApplication(applicationName).GetOperation(operationName);
            if (operation == null)
            {
                throw new AuthorizationException("UserAuthorizationService.AccessCheck", string.Format(Resources.operation_not_found, operationName));
            }
            
            return GetUserAuthorization(userName, applicationName).AccessCheck(operation, scopeName);
        }

        public static int[] AccessCheck(string userName, string applicationName, int[] operationIds, string[] scopeNames)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");

            if (operationIds == null)
            {
                throw new ArgumentNullException("operationIds");
            }

            return GetUserAuthorization(userName, applicationName).AccessCheck(operationIds, scopeNames);
        }

        //public static OperationCollection AccessCheck(string userName, string applicationName, string[] operationNames, string[] scopeNames)
        //{
        //    SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
        //    SecUtility.CheckArrayParameter(ref operationNames, true, true, false, 64, "operationNames");

        //    Application application = AuthorizationStoreManager.GetApplication(applicationName);

        //    var operations = new OperationCollection();

        //    foreach (var operationName in operationNames)
        //    {
        //        operations.Add(application.GetOperation(operationName));
        //    }

        //    return GetUserAuthorization(userName, applicationName).AccessCheck(operations, scopeNames);
        //}


        // TODO: CRIAR ACCESSCHECKS COM ROLE/APPLICATIONGROUP RESTRICTIONS
        public static OperationCollection AccessCheck(string userName, string applicationName, 
            string[] operationNames, string[] scopeNames)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            SecUtility.CheckArrayParameter(ref operationNames, true, true, false, 64, "operationNames");

            Application application = AuthorizationStoreManager.GetApplication(applicationName);

            if (application == null) throw new NullReferenceException("Application not found");

            var operations = new OperationCollection();

            foreach (var operationName in operationNames)
            {
                operations.Add(application.GetOperation(operationName));
            }

            return GetUserAuthorization(userName, applicationName).AccessCheck(operations, scopeNames);
        }

        #endregion

        #endregion

        #region Private Methods

        private static AuthorizationContext GetUserAuthorizationByCustomSid(string[] Sids, string applicationName)
        {
            SecUtility.CheckArrayParameter(ref Sids, true, true, false, 64, "Sids");
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            return AuthorizationStoreManager.GetApplication(applicationName).GetAuthorizationContextByCustomSID(Sids);
        }

        private static AuthorizationContext GetUserAuthorization(string userName, string applicationName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");

            Application application = AuthorizationStoreManager.GetApplication(applicationName);

            if (application == null)
                throw new AuthorizationException(SR.GetString("invalid_application"));

            if (OperationContext.Current == null)   // No host, no acting as service
            {
                if (userName.StartsWith("S-1-", StringComparison.InvariantCultureIgnoreCase))
                    return application.GetAuthorizationContextByCustomSID(new string[] { userName });
                else
                    return application.GetAuthorizationContext(userName, null);
            }

            // gets from UserName
            if (!string.IsNullOrEmpty(userName))
            {
                if (ServiceSecurityContext.Current != null && !ServiceSecurityContext.Current.IsAnonymous)
                {
                    if (userName.StartsWith("S-1-", StringComparison.InvariantCultureIgnoreCase))
                    {
                         return application.GetAuthorizationContextByCustomSID(new[] { userName });
                    }
                    else
                        return application.GetAuthorizationContext(userName,
                                                                                                             null);
                }

                throw new AuthorizationException(SR.GetString("UserAuthorization_user_must_be_authenticated"));
            }

            // Gets from ServiceSecurityContext 
            if (ServiceSecurityContext.Current != null)
            {
                return
                    ServiceSecurityContext.Current.WindowsIdentity != null
                        ? AuthorizationStoreManager.GetApplication(applicationName).GetAuthorizationContext(
                              ServiceSecurityContext.Current.WindowsIdentity.Token)
                        : AuthorizationStoreManager.GetApplication(applicationName).GetAuthorizationContext(
                              ServiceSecurityContext.Current.PrimaryIdentity.Name, null);
            }

            throw new AuthorizationException(SR.GetString("UserAuthorization_user_must_be_authenticated"));


        }

        public static bool IsUserInApplicationGroup(string userName, string applicationName, string scopeName, string applicationGroupName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");
            SecUtility.CheckParameter(ref applicationGroupName, true, true, false, 64, "applicationGroupName");

            using (var context = GetUserAuthorization(userName, applicationName))
            {
                return context.IsInApplicationGroup(applicationGroupName, scopeName);
            }

        }

        #endregion



        
    }
}

